package com.xzp.gkrpc.client;

import com.xzp.gkrpc.Request;
import com.xzp.gkrpc.Response;
import com.xzp.gkrpc.ServiceDescriptor;
import com.xzp.gkrpc.codec.Decoder;
import com.xzp.gkrpc.codec.Encoder;
import com.xzp.gkrpc.transprot.TransportClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 调用远程服务的代理类
 */
@Slf4j
public class RemoteInvoker implements InvocationHandler {
    private Class clazz;
    private Encoder encoder;
    private Decoder decoder;
    private TransportSelector selector;

    public RemoteInvoker(Class clazz, Encoder encoder, Decoder decoder, TransportSelector selector){
        this.clazz = clazz;
        this.encoder = encoder;
        this.decoder = decoder;
        this.selector = selector;
    }

    /**
     * 调用远程方法就像调用本地方法一样
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 封装一个request对象
        Request request = new Request();
        ServiceDescriptor descriptor = ServiceDescriptor.from(this.clazz, method);
        request.setService(descriptor);
        request.setParameters(args);
        // 先将request序列化
        // 将request对象发送给对面
        Response resp = invokeRemote(request);
        // 调用失败
        if(resp == null || resp.getCode() != 0){
            throw new IllegalStateException("fail to invoke remote:" + resp.getMessage());
        }
        // 返回调用结果
        return resp.getData();
    }

    public Response invokeRemote(Request request){
        TransportClient client = null;
        Response response = null;
        byte[] req_bytes = encoder.encode(request);
        try{
            // 从selector连接池中随机选一个（需要保证不同的peer上服务相同）
            client = selector.select();
            // 返回接收到的数据
            InputStream inputStream = client.write(new ByteArrayInputStream(req_bytes));
            // 读取数据 恢复成对象
            byte[] receive_bytes = IOUtils.readFully(inputStream, inputStream.available());
            response = decoder.decode(receive_bytes, Response.class);
            System.out.println(response);
        } catch (IOException e) {
            log.warn("invokeRemote error");
            log.warn(e.getMessage(), e);
            response = new Response();
            response.setCode(1);
            response.setMessage("RpcClient got Error" +
                    e.getClass().getName() +
                    ":" +e.getMessage());
        }finally {
            // 保证服务调用不会丢失
            if(client!=null){
                selector.release(client);
            }
            System.out.println(response);
        }
        return response;
    }
}
